package fire.and.near.challenger.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.spi.CharsetProvider;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import fire.and.near.challenger.database.ChallengeDatabaseHelper;
import fire.and.near.challenger.database.ChallengeModel;
import fire.and.near.challenger.database.ChallengeModel.ChallengeType;
import fire.and.near.challenger.database.ChallengeStatus;
import fire.and.near.challenger.database.DefaultChallengesManager;
import fire.and.near.challenger.database.ProfileManager;
import fire.and.near.challenger.util.Consts;
import fire.and.near.challenger.web.ServerResponse.RequestType;

public class HttpRequestSender extends AsyncTask<Object, Void, ServerResponse> {

	private String SERVER_URL 			= "http://challengerserver.herokuapp.com/api/challenges";
	private String UPDATE_SERVER_URL 	= "http://challengerserver.herokuapp.com/api/update";
	private String ACCESS_TOKEN = "silensifire";
	private RequestOptions option = null;
	private ServerResponse response = null;
	private Activity context = null;

	public HttpRequestSender(Activity context) {
		response = new ServerResponse();
		this.context = context;
	}

	public HttpRequestSender(RequestOptions option, Activity context) {
		this.option = option;
		this.response = new ServerResponse();
		this.context = context;
	}

	public enum RequestOptions {
		DOWNLOAD_CHALLENGES, UPLOAD_CHALLENGES, UPDATE_CHALLENGES
	}

	@Override
	protected void onPreExecute() {

		// Progress bar?
		if (this.context != null) {
			((WebActivity) this.context).setHttpWorking(true);
		}

		super.onPreExecute();
	}

	@Override
	protected ServerResponse doInBackground(Object... params) {
		this.response.setRequestOption(this.option);

		if (params.length == 0 || params[0] == null) {
			return null;
		}

		switch (this.option) {
		case DOWNLOAD_CHALLENGES:
			return sendPostRequest(params[0]);
		case UPLOAD_CHALLENGES:
			return sendPutRequest(params[0]);
		case UPDATE_CHALLENGES:
			ServerResponse response = updateDefaultChallenges(params[0]);
			if (response.getStatusCode() == 200
					&& !((List<ChallengeModel>) response.getData()).isEmpty()) {
				insertDefaultChallengesIntoDatabase((List<ChallengeModel>) response
						.getData());
			}
			return response;
		}

		return null;
	}

	@Override
	protected void onPostExecute(ServerResponse result) {

		// Progress bar?
		if (this.context != null) {
			((WebActivity) this.context).processSeverResponse(result);
			((WebActivity) this.context).setHttpWorking(false);
		}
		
		super.onPostExecute(result);
	}

	private ServerResponse sendPutRequest(Object data) {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPut httpPut = null;
		HttpResponse response = null;

		@SuppressWarnings("unchecked")
		JSONArray jsonArr = challengesToJson((List<ChallengeModel>) data);

		httpPut = new HttpPut(SERVER_URL);

		StringEntity se = null;

		try {
			se = new StringEntity(jsonArr.toString(), HTTP.UTF_8);

		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}

		httpPut.setHeader("Accept", "application/json");
		httpPut.setHeader("Content-type", "application/json");
		httpPut.setEntity(se);

		try {
			response = httpclient.execute(httpPut);
		} catch (ClientProtocolException e) {
			Log.i("APPHB PUT", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.PUT);

			return this.response;
		} catch (IOException e) {
			Log.i("APPHB PUT", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.PUT);

			return this.response;
		}

		this.response.setStatusCode(response.getStatusLine().getStatusCode());
		this.response.setRequestType(RequestType.PUT);

		if (response.getStatusLine().getStatusCode() == 201) {
			return this.response;
		} else {
			Log.v("APPHB PUT", "FAILED WITH HSC: "
					+ response.getStatusLine().getStatusCode());
			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.PUT);

			return this.response;
		}
	}

	private ServerResponse sendPostRequest(Object data) {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost httpPost = null;
		HttpResponse response = null;

		JSONObject json = credentialsToJson((Long) data);

		httpPost = new HttpPost(SERVER_URL);

		StringEntity se = null;

		try {
			se = new StringEntity(json.toString(), HTTP.UTF_8);

		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}

		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Content-type", "application/json");
		httpPost.setEntity(se);

		try {
			response = httpclient.execute(httpPost);
		} catch (ClientProtocolException e) {
			Log.i("APPHB POST", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		} catch (IOException e) {
			Log.i("APPHB POST", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		}

		Object responseData = getChallengesFromJson(getResponseContent(response));

		this.response.setStatusCode(response.getStatusLine().getStatusCode());
		this.response.setData(responseData);
		this.response.setRequestType(RequestType.POST);

		if (response.getStatusLine().getStatusCode() == Consts.HTTP_OK) {
			return this.response;
		} else {
			Log.v("APPHB POST", "FAILED WITH HSC: "
					+ response.getStatusLine().getStatusCode());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		}
	}

	private ServerResponse updateDefaultChallenges(Object data) {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost httpPost = null;
		HttpResponse response = null;

		JSONObject json = createUpdateRequuest((Long) data);

		httpPost = new HttpPost(UPDATE_SERVER_URL);

		StringEntity se = null;

		try {
			se = new StringEntity(json.toString(), HTTP.UTF_8);
			Log.i("JSON UPDATE", json.toString());

		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}

		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Content-type", "application/json");
		httpPost.setEntity(se);

		try {
			response = httpclient.execute(httpPost);
		} catch (ClientProtocolException e) {
			Log.i("APPHB POST", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		} catch (IOException e) {
			Log.i("APPHB POST", e.getMessage());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		}

		Object responseData = getDefaultChallengesFromJson(getResponseContent(response));

		this.response.setStatusCode(response.getStatusLine().getStatusCode());
		this.response.setData(responseData);
		this.response.setRequestType(RequestType.POST);

		if (response.getStatusLine().getStatusCode() == Consts.HTTP_OK) {
			return this.response;
		} else {
			Log.v("APPHB POST", "FAILED WITH HSC: "
					+ response.getStatusLine().getStatusCode());

			this.response.setStatusCode(-1);
			this.response.setRequestType(RequestType.POST);

			return this.response;
		}
	}

	private String getResponseContent(HttpResponse response) {
		HttpEntity responseEntity = response.getEntity();
		InputStream inStream = null;

		try {
			inStream = responseEntity.getContent();
		} catch (IllegalStateException e) {
		} catch (IOException e) {
		}

		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(
					inStream, "UTF-8"));
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		StringBuilder out = new StringBuilder();
		String line;
		try {
			while ((line = reader.readLine()) != null) {
				out.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return out.toString();
	}

	private JSONArray challengesToJson(List<ChallengeModel> challenges) {

		JSONArray jsonArr = new JSONArray();

		for (int i = 0; i < challenges.size(); i++) {
			JSONObject json = new JSONObject();
			try {
				json.put("Token", ACCESS_TOKEN);
				json.put("Description", challenges.get(i).getDescription());
				json.put("Type", challenges.get(i)
						.getChallengeTypeForDatabase());
				json.put("From", challenges.get(i).getChallenger().getFbID());
				json.put("To", challenges.get(i).getChallengedID());
			} catch (JSONException e) {
				e.printStackTrace();
			}
			jsonArr.put(json);

			Log.i("JSON WE", jsonArr.toString());
		}

		return jsonArr;
	}

	private JSONObject credentialsToJson(long challengedId) {
		JSONObject json = new JSONObject();
		try {
			json.put("Token", ACCESS_TOKEN);
			json.put("UserID", challengedId);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return json;
	}

	private JSONObject createUpdateRequuest(long challengeId) {
		JSONObject json = new JSONObject();
		try {
			json.put("Id", challengeId);
			json.put("Token", ACCESS_TOKEN);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return json;
	}

	private List<ChallengeModel> getChallengesFromJson(String json) {
		List<ChallengeModel> challenges = new ArrayList<ChallengeModel>();

		JSONArray jarr = null;

		try {
			jarr = new JSONArray(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}

		try {
			for (int i = 0; i < jarr.length(); i++) {

				String description = ((JSONObject) jarr.get(i))
						.getString("description");
				int type = ((JSONObject) jarr.get(i)).getInt("type");
				ChallengeType chType = null;
				if (type == 1) {
					chType = ChallengeType.TEXT;
				} else if (type == 2) {
					chType = ChallengeType.PHOTO;
				} else {
					chType = ChallengeType.TEXT;
				}

				long challengerId = ((JSONObject) jarr.get(i))
						.getLong("challengerId");
				long challengedId = ((JSONObject) jarr.get(i))
						.getLong("challengedId");

				ChallengeModel ch = new ChallengeModel(0, description, null,
						chType, null, challengerId, challengedId, true,
						ChallengeStatus.IN_MY_CHALLENGES);

				ch.setHashtag(Consts.DEFAULT_HASHTAG);

				challenges.add(ch);
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}

		return challenges;
	}

	private List<ChallengeModel> getDefaultChallengesFromJson(String json) {
		List<ChallengeModel> challenges = new ArrayList<ChallengeModel>();

		JSONArray jarr = null;

		try {
			jarr = new JSONArray(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}

		try {
			for (int i = 0; i < jarr.length(); i++) {

				int challengeId = ((JSONObject) jarr.get(i)).getInt("_id");
				String description = ((JSONObject) jarr.get(i))
						.getString("description");
				int type = ((JSONObject) jarr.get(i)).getInt("type");
				ChallengeType chType = null;
				if (type == 1) {
					chType = ChallengeType.TEXT;
				} else if (type == 2) {
					chType = ChallengeType.PHOTO;
				} else {
					chType = ChallengeType.TEXT;
				}
				String hashTag = ((JSONObject) jarr.get(i))
						.getString("hashtag");

				if (context == null) {
					return null;
				}
				
				ProfileManager pManager = new ProfileManager(context);
				long myFbId = pManager.getFbId();

				ChallengeModel ch = new ChallengeModel(0, description, null,
						chType, null, challengeId, myFbId, false,
						ChallengeStatus.IN_DATABASE);

				ch.setHashtag(hashTag);

				challenges.add(ch);
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}

		return challenges;
	}

	private void insertDefaultChallengesIntoDatabase(
			List<ChallengeModel> challenges) {
		if (context == null) {
			return;
		}
		
		ChallengeDatabaseHelper helper = new ChallengeDatabaseHelper(context);
		helper.insertChallenges(challenges);
		helper.close();

		DefaultChallengesManager
				.updateLastDefaultChallengeId(context,
						challenges.get(challenges.size() - 1).getChallenger()
								.getFbID());
	}

	public ServerResponse getResponse() {
		return response;
	}

	public void setResponse(ServerResponse response) {
		this.response = response;
	}

	public static boolean isNetworkAvailable(Activity activity) {
		ConnectivityManager connectivityManager = (ConnectivityManager) activity
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo activeNetworkInfo = connectivityManager
				.getActiveNetworkInfo();
		return activeNetworkInfo != null && activeNetworkInfo.isConnected();
	}

	public static void connectingToServerErrorMessage(Activity activity) {
		Toast.makeText(
				activity,
				"Could not connect to server, please check your internet connection.",
				Toast.LENGTH_LONG).show();
	}
}
